package com.kalua.list.dbaccess.retailer;

import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
import static com.datastax.driver.core.querybuilder.QueryBuilder.in;
import static com.datastax.driver.core.querybuilder.QueryBuilder.set;
import static com.kalua.list.dbaccess.retailer.CrawlBatchInfoRowMapper.BATCH_ID_COL_NAME;
import static com.kalua.list.dbaccess.retailer.CrawlBatchInfoRowMapper.CRAWL_BATCH_INFO_COL_FAMILY;
import static com.kalua.list.dbaccess.retailer.CrawlBatchInfoRowMapper.CREATE_COUNT_COL_NAME;
import static com.kalua.list.dbaccess.retailer.CrawlBatchInfoRowMapper.INTERNAL_FAILURE_COUNT_COL_NAME;
import static com.kalua.list.dbaccess.retailer.CrawlBatchInfoRowMapper.NON_VIABLE_COUNT_COL_NAME;
import static com.kalua.list.dbaccess.retailer.CrawlBatchInfoRowMapper.PREVIOUS_BATCH_ID_COL_NAME;
import static com.kalua.list.dbaccess.retailer.CrawlBatchInfoRowMapper.PRODUCT_COUNT_COL_NAME;
import static com.kalua.list.dbaccess.retailer.CrawlBatchInfoRowMapper.TIME_COMPLETED_COL_NAME;
import static com.kalua.list.dbaccess.retailer.CrawlBatchInfoRowMapper.TIME_STARTED_COL_NAME;
import static com.kalua.list.dbaccess.retailer.CrawlBatchInfoRowMapper.UPDATE_COUNT_COL_NAME;
import static com.kalua.list.dbaccess.retailer.RetailerRowMapper.RETAILER_ID_COL_NAME;
import static java.util.stream.Collectors.toList;

import java.util.Collection;
import java.util.Date;

import org.springframework.cassandra.core.RowMapper;
import org.springframework.data.cassandra.core.CassandraTemplate;

import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.Statement;
import com.datastax.driver.core.querybuilder.Insert;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.kalua.list.dbaccess.AbstractCRUDDAO;
import com.kalua.list.domainmodel.NumericId;
import com.kalua.list.domainmodel.TextIdentifier;
import com.kalua.list.domainmodel.retailer.CrawlBatchInfo;

public class CrawlBatchInfoDAOImpl 	extends AbstractCRUDDAO<CrawlBatchInfo, NumericId>
															implements CrawlBatchInfoDAO {
	
	private static final String LATEST_BATCH_CRAWL_COL_FAMILY = "latest_batch_crawl";
	
	public CrawlBatchInfoDAOImpl(CassandraTemplate template) 
	{ super(template); }
	
	@Override
	public CrawlBatchInfo findLatest(TextIdentifier retailerId) {
		CrawlBatchInfo result;
		
		NumericId latestId = findLatestBatchId(retailerId);
		if (latestId != null)
			result = read(latestId);
		else result = null;
		return result;
	}
	
	@Override
	protected Class<NumericId> getIdType() { return NumericId.class; }

	@Override
	protected Statement buildInsertStatement(CrawlBatchInfo entity) {
		Insert insert =  
				QueryBuilder	.insertInto(CRAWL_BATCH_INFO_COL_FAMILY)
										.value(BATCH_ID_COL_NAME, entity.getId().getValue())
			        					.value(PRODUCT_COUNT_COL_NAME, entity.getNumberOfProductsExtracted())
			        					.value(PREVIOUS_BATCH_ID_COL_NAME, previousBatchIdFrom(entity))
			        					.value(RETAILER_ID_COL_NAME, entity.getRetailerId().getValue())
			        					.value(TIME_STARTED_COL_NAME, Date.from(entity.getTimeStarted()))
			        					.value(TIME_COMPLETED_COL_NAME, timeCompletedFrom(entity))
										.value(UPDATE_COUNT_COL_NAME, entity.getUpdateCount())
										.value(CREATE_COUNT_COL_NAME, entity.getCreateCount())
										.value(INTERNAL_FAILURE_COUNT_COL_NAME, entity.getInternalFailureCount())
										.value(NON_VIABLE_COUNT_COL_NAME, entity.getNonViableCount())
										.ifNotExists();
		return insert;
	}

	protected Long previousBatchIdFrom(CrawlBatchInfo entity) {
		return entity.getPreviousBatchId()!=null ? 
				entity.getPreviousBatchId().getValue() : null;
	}

	@Override
	protected Statement buildMultiRowSelect(Object[] keys) {
		Statement statement = QueryBuilder.select().all()
				.from(CRAWL_BATCH_INFO_COL_FAMILY)
				.where(in(BATCH_ID_COL_NAME, keys));
		return statement;
	}

	@Override
	protected Statement buildReadStatement(NumericId id) {
		Statement statement = QueryBuilder.select().all()
				.from(CRAWL_BATCH_INFO_COL_FAMILY)
				.where(eq(BATCH_ID_COL_NAME, id.getValue()));
		return statement;
	}

	@Override
	protected Statement buildReadAllStatement() {
		Statement statement = 
				QueryBuilder	.select().all()
										.from(CRAWL_BATCH_INFO_COL_FAMILY);
		return statement;
	}

	@Override
	protected Statement buildUpdateStatement(CrawlBatchInfo entity) {
		Statement update =  
				QueryBuilder	.update(CRAWL_BATCH_INFO_COL_FAMILY)
			        					.with(set(PRODUCT_COUNT_COL_NAME, entity.getNumberOfProductsExtracted()))
			        					.and(set(PREVIOUS_BATCH_ID_COL_NAME, previousBatchIdFrom(entity)))
			        					.and(set(RETAILER_ID_COL_NAME, entity.getRetailerId().getValue()))
			        					.and(set(TIME_STARTED_COL_NAME, Date.from(entity.getTimeStarted())))
			        					.and(set(TIME_COMPLETED_COL_NAME, timeCompletedFrom(entity)))
										.and(set(UPDATE_COUNT_COL_NAME, entity.getUpdateCount()))
										.and(set(CREATE_COUNT_COL_NAME, entity.getCreateCount()))
										.and(set(INTERNAL_FAILURE_COUNT_COL_NAME, entity.getInternalFailureCount()))
										.and(set(NON_VIABLE_COUNT_COL_NAME, entity.getNonViableCount()))
			        					.where(eq(BATCH_ID_COL_NAME, entity.getId().getValue()));
		return update;
	}

	@Override
	protected Statement buildDeleteStatement(NumericId id) {
		Statement statement = 
				QueryBuilder.delete()
		        		.from(CRAWL_BATCH_INFO_COL_FAMILY)
		        		.where(eq(BATCH_ID_COL_NAME, id.getValue()));
		return statement;
	}

	@Override
	protected RowMapper<CrawlBatchInfo> getRowMapper() 
	{ return new CrawlBatchInfoRowMapper(); }

	@Override
	protected Object[] toObjectArray(Collection<NumericId> keys) {
		Object [] result = 
				keys.stream().map(n -> n.getValue()).collect(toList()).toArray();
		return result; 
	}
	
	@Override
	protected void postCreate(CrawlBatchInfo entity) {
		Statement statement = 
				QueryBuilder	.update(LATEST_BATCH_CRAWL_COL_FAMILY)
										.with(set(BATCH_ID_COL_NAME, entity.getId().getValue()))
		        						.where(eq(RETAILER_ID_COL_NAME, entity.getRetailerId().getValue()));
		executeStatement(statement);
	}
	
	private NumericId findLatestBatchId(TextIdentifier retailerId) {
		NumericId result;
		Statement statement = 
				QueryBuilder	.select().all()
										.from(LATEST_BATCH_CRAWL_COL_FAMILY)
										.where(eq(RETAILER_ID_COL_NAME, retailerId.getValue()));
		
		ResultSet resultSet = getTemplate().getSession().execute(statement);
		if (!resultSet.isExhausted()) {
			Row row = resultSet.one();
			result = new NumericId(row.getLong(BATCH_ID_COL_NAME));
		} else result = null;
		
		return result;
	}
	

	private Object timeCompletedFrom(CrawlBatchInfo entity) {
		return entity.getTimeCompleted()!=null ?
				Date.from(entity.getTimeCompleted()) : null;
	}
}
